package org.epsi.blokus.server;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import org.epsi.blokus.commun.HexException;
import org.epsi.blokus.commun.Message;
import org.epsi.blokus.commun.Message.TTypeMessage;

/**
 * Gestionnaire de salons
 * @author julien.lafont
 */
public class SalonManager  {

    private static Integer indexSalons = 0;
    
    private ArrayList<ServerClient> listeClients = new ArrayList<ServerClient>();
    public HashMap<Integer, Salon> listeSalons = new HashMap<Integer, Salon>();

    /**
     * Un nouveau client entre dans la partie
     * @param sc
     */
    public void nouveauClient(ServerClient sc)
    {
        listeClients.add(sc);

        // On informe le joueur de la liste des salons
        sc.envoyerMessage(new Message(TTypeMessage.connexion_client_ack).addObject("LISTSALONS", this.getListeSalons()));
    }

    /**
     * Un client quitte la partie
     * @param sc
     */
    public void retirerClient(ServerClient sc)
    {
        // Quite la salon si il est dans un salon
        this.quitterSalon(sc);

        // Accusé de réception
        sc.envoyerMessage(new Message(TTypeMessage.deconnexion_client_ack));
        sc.deconnecter();

        // Supprime le client de la liste
        listeClients.remove(sc);
    }

    /**
     * Un client demande la création d'un nouveau salon
     * @param source
     * @return
     */
    public Integer creerSalon(ServerClient source, String nom) {
     
        Integer index = indexSalons++;

        // Ajoute le salon
        Salon s = new Salon(source, index, nom);
        this.listeSalons.put(index, s);
        source.setSalon(s);

        // Envoie l'accusé de réception
        source.envoyerMessage(
            new Message(TTypeMessage.creation_salon_ack)
            .addObject("IDSALON", index.toString())
            .addObject("MAITRE", "Client "+source.getNumero())
            .addObject("NOMSALON",nom)
        );

        // Informe tous les clients de ce nouveau salon
        this.envoyerMessageTousLesJoueur(
                new Message(TTypeMessage.ouverture_salon)
                    .addObject("IDSALON", index.toString())
                    .addObject("MAITRE", "Client "+source.getNumero())
                    .addObject("NOMSALON", nom)
        );

        return index;
    }

    /**
     * Un client tente de rejoindre un salon
     * @param invite
     * @param index
     */
    public void rejoindreSalon(ServerClient invite, Integer index) throws IndexOutOfBoundsException, HexException
    {
        
        // Récupère le salon visé (si il existe )
        Salon salon = this.listeSalons.get(index);

        // Salon libre ?
        if (salon.getNbJoueurs()>1) throw new HexException("Le salon est complet");

        // Lie le client au salon
        salon.ajouterInvite(invite);
        invite.setSalon(salon);

        // Envoie l'accusé de réception au client
        invite.envoyerMessage(new Message(TTypeMessage.entrer_salon_ack).addObject("IDSALON", index.toString()));

        // Avertit le maitre du nouvel invité
        this.listeSalons.get(index).getMaitre().envoyerMessage(new Message(TTypeMessage.entrer_salon_ack).addObject("INVITE", "Client "+invite.getNumero()));

    }
    
    /**
     * Un client quite son salon (maitre ou invité)
     * @param client
     */
    public void quitterSalon(ServerClient client)
    {
        Salon salon = client.getSalon();
        if (salon != null)
        {
            // Si il est maitre, on ferme le salon
            Boolean etait_maitre = salon.retirerClient(client);
            if (etait_maitre)
            {
                
                fermerSalon(client);

            }
            
            // Sinon on avertit le maitre de son départ
            else
            {
                client.getSalon().getMaitre().envoyerMessage(new Message(TTypeMessage.sortir_salon_ack).addObject("INVITE", "Client "+client.getNumero()));
            }
        }


    }

    /*
     * Fermer un salon donné
     */
    public void fermerSalon(ServerClient client)
    {
        Salon salon = client.getSalon();
        this.listeSalons.remove(salon.getIndex());

        // Fermer le salon, avertir l'invité
        salon.fermer();

        // On avertit tout le monde de la suppression du salon
        this.envoyerMessageTousLesJoueur(
                new Message(TTypeMessage.maj_liste_salons).addObject("LISTSALONS", this.getListeSalons()));
    }


    /**
     * Retransmet un message à tous les joueurs connectés
     * @param m
     */
    public void envoyerMessageTousLesJoueur(Message m ){
        for(ServerClient sc : this.listeClients) sc.envoyerMessage(m);
    }


    /**
     * Retransmet un message à tous les joueurs sauf UN
     * @param m
     * @param except
     */
    public void envoyerMessageTousLesJoueursSauf(Message m, ServerClient except)
    {
        for(ServerClient sc : this.listeClients)
        {
            if (!sc.equals(except)) sc.envoyerMessage(m);
        }
    }


    /**
     * Retourne la liste des salons sous forme de liste de string
     * @return
     */
    public ArrayList<String> getListeSalons()
    {
        ArrayList<String> liste = new ArrayList<String>();
        Iterator it = this.listeSalons.entrySet().iterator();
        while (it.hasNext()) liste.add(((Salon)it.next()).toString());
        return liste;
    }


    public int getNbJoueurs()
    {
        return this.listeClients.size();
    }

    public int getNbSalons()
    {
        return this.listeSalons.size();
    }

    /**
     * Retourne le nombre de joueurs dans le salon donnée
     */
    public int getNbJoueursDansSalon(Integer idSalon) {
        Salon s = listeSalons.get(idSalon);
        if (s==null) return -1;
        else return s.getNbJoueurs();
    }


}
